use strict;
use warnings;

package My::Form::Helper::Generator;
use base qw(Exporter);
our @EXPORT_OK =
    qw(generate_form_html generate_form_elements_html generate_field_html generate_label_html generate_tip_html)
    ;    # symbols to export on request
use Carp qw(confess);
use CGI;
use My::Form::Helper::Tools qw(eval_value flatten_form_elements init_fields set_html_attributes_on_RHTML);
my $tip_class            = 'tip';
my $compound_field_class = 'compound_field';

sub generate_form_html {
}

sub generate_form_elements_html {
    my %params          = @_;
    my $RHTMLO_form     = $params{RHTMLO_form};
    my $elements        = $params{elements};
    my $session_hashref = $params{session_hashref};
    my $next_form       = $params{next_form};
    my $html            = '';
    my @field_elements_list;
    my $total_elements = scalar( @{$elements} );
    my $element_index  = 0;

    foreach my $element_params ( @{$elements} ) {
        $element_params->{form} = $RHTMLO_form;
        $element_index++;
        my $cur_element_html     = '';
        my $cur_element_is_field = undef;
        if ( exists( $element_params->{field_name} ) ) {
            if ( ( defined( $element_params->{condition} )
                   && eval_value( init_values => [ %{$session_hashref} ],
                                  value       => $element_params->{condition} )
                 )
                 || ( !defined( $element_params->{condition} ) )
                )
            {
                $cur_element_html
                    .= generate_field_html( %{$element_params}, session_hashref => $session_hashref );
                $cur_element_is_field = 1;    # a field element will be a part of a list
            }
        } else {
            if ( exists( $element_params->{fieldset} ) && ( $element_params->{fieldset} ) ) {
                my $fieldset_html = '';
                $fieldset_html = CGI->legend( eval_value( init_values => [ %{$session_hashref} ],
                                                          value       => $element_params->{legend}
                                              )
                ) if ( $element_params->{legend} );

                # initialise the parameters we'd need for a nested call
                my %nested_params;
                $nested_params{RHTMLO_form}     = $RHTMLO_form;
                $nested_params{elements}        = $element_params->{elements};
                $nested_params{session_hashref} = $session_hashref;
                $fieldset_html .= generate_form_elements_html(%nested_params);
                $cur_element_html .= CGI->fieldset($fieldset_html);
                $cur_element_is_field = undef;
            } ## end if ( exists( $element_params->{fieldset} )...
            elsif ( exists( $element_params->{heading_text} )
                    && ( $element_params->{heading_text} ) )
            {
                $cur_element_html .= CGI->h1( eval_value( init_values => [ %{$session_hashref} ],
                                                          value       => $element_params->{heading_text}
                                              )
                );
            } elsif ( exists( $element_params->{subheading_text} )
                      && ( $element_params->{subheading_text} ) )
            {
                $cur_element_html .= CGI->h3( eval_value( init_values => [ %{$session_hashref} ],
                                                          value       => $element_params->{subheading_text}
                                              )
                );
            } elsif ( exists( $element_params->{text} ) && ( $element_params->{text} ) ) {
                $cur_element_html .= CGI->p( eval_value( init_values => [ %{$session_hashref} ],
                                                         value       => $element_params->{text}
                                             )
                );
            }
        } ## end else [ if ( exists( $element_params->{field_name}...
        if ($cur_element_is_field) {
            push @field_elements_list, $cur_element_html;
            undef $cur_element_html;
        }

        # if it isn't, or if it is the final element, we flush the list and render it as html
        if ( !$cur_element_is_field || $element_index == $total_elements ) {
            if ( scalar(@field_elements_list) ) {
                $html .= CGI->ol(@field_elements_list);
                @field_elements_list = ();
            }
            $html .= $cur_element_html;
            undef $cur_element_html;
        } ## end if ( !$cur_element_is_field || $element_index...
    } ## end foreach my $element_params ( @{$elements} )
    return $html;
} ## end sub generate_form_elements_html

sub generate_field_html {
    my %params = @_;
    my $form   = $params{form};
    delete $params{form};
    my $session_hashref = $params{session_hashref};
    delete $params{session_hashref};
    my $field_name = eval_value( init_values => [ %{$session_hashref} ], value => $params{field_name} );
    delete $params{field_name};
    my $class = $params{class};
    delete $params{class};
    my $label = eval_value( init_values => [ %{$session_hashref} ], value => $params{label} );
    delete $params{label};
    my $tip = eval_value( init_values => [ %{$session_hashref} ], value => $params{tip} );
    delete $params{tip};
    my $compound_field = $params{compound_field};
    delete $params{compound_field};
    my %html_attributes = %{ $params{attributes} || {} };
    delete $params{attributes};

    if ( !defined( $form->field($field_name) ) ) {
        confess("$field_name is not defined on RHTMLO form");
    }

    # parameters that we don't need for html generation
    delete $params{save_to};
    my @classes = split( /\s+/, $class );
    if ($compound_field) {
        push @classes, $compound_field_class;
        $params{-class} = $compound_field_class;
    }
    if ( $form->field($field_name)->required() ) {
        push @classes, 'required_field';
    }
    $form->field($field_name)->html_attr( 'class', join( ' ', @classes ) )
        if ( scalar(@classes) );
    set_html_attributes_on_RHTML( $form->field($field_name), %html_attributes );
    my $html = generate_label_html( $form, $field_name, $label, $tip );
    $html .= ( $compound_field
               ? CGI->span( \%params, $form->field($field_name)->html )
               : $form->field($field_name)->html
    );

    # wraps field in a div
    $html = CGI->li( \%params, $html );
    return $html;
} ## end sub generate_field_html

sub generate_label_html {
    my ( $form, $field_name, $label, $tip ) = @_;
    my $field = $form->field($field_name);
    if ( defined($label) ) {

        # leave it as it is
    } else {
        $label = $field->label() || $field->name();
    }
    return ( CGI->label() ) if ( !$label );
    if ( $label && !$tip ) {
        my %params;

        # updates label value for field
        $form->field($field_name)->label($label);
        return $form->field($field_name)->html_label;
    } ## end if ( $label && !$tip )
    elsif ($tip) {

        # in this case we'd have to use our own workaround with CGI HTML generation
        my %params;
        my $for = $form->field($field_name)->html_attr('name');
        $params{'for'} = $for if ($for);
        $label = ( $label ? $label : $form->field($field_name)->label() );
        if ($label) {
            return CGI->label( \%params, $label . generate_tip_html($tip) );
        }
    } ## end elsif ($tip)
} ## end sub generate_label_html

sub generate_tip_html {
    my ($tip) = @_;
    return '' if ( !$tip );
    return CGI->span( { -class => $tip_class }, $tip );
} ## end sub generate_tip_html
1;
